package sk.tramtatiri.pages.users;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ofc4j.model.Chart;
import ofc4j.model.axis.Label.Rotation;
import ofc4j.model.axis.XAxis;
import ofc4j.model.axis.XAxisLabels;
import ofc4j.model.axis.YAxis;
import ofc4j.model.elements.AreaHollowChart;
import ofc4j.model.elements.BarChart;
import ofc4j.model.elements.BarChart.Style;
import ofc4j.model.elements.LineChart;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.validation.validator.MinimumValidator;
import org.apache.wicket.validation.validator.RangeValidator;

import sk.tramtatiri.MySession;
import sk.tramtatiri.OpenFlashChart;
import sk.tramtatiri.core.CustomizedBeautyTip;
import sk.tramtatiri.data.dao.interfaces.FoodDao;
import sk.tramtatiri.data.dao.interfaces.InOutHelperDao;
import sk.tramtatiri.data.dao.interfaces.RecipeDao;
import sk.tramtatiri.data.dao.interfaces.SportDao;
import sk.tramtatiri.data.dao.interfaces.UserDao;
import sk.tramtatiri.data.dao.interfaces.UserFoodDao;
import sk.tramtatiri.data.dao.interfaces.UserRecipeDao;
import sk.tramtatiri.data.dao.interfaces.UserSportDao;
import sk.tramtatiri.data.dataobjects.Food;
import sk.tramtatiri.data.dataobjects.InOutHelper;
import sk.tramtatiri.data.dataobjects.Recipe;
import sk.tramtatiri.data.dataobjects.Sport;
import sk.tramtatiri.data.dataobjects.User;
import sk.tramtatiri.data.dataobjects.UserFood;
import sk.tramtatiri.data.dataobjects.UserRecipe;
import sk.tramtatiri.data.dataobjects.UserSport;

public class MealActivityTrackerPanel extends Panel {

	private static final long serialVersionUID = 8704946062036183758L;

	@SpringBean
	private UserDao userDao;
	@SpringBean
	private FoodDao foodDao;
	@SpringBean
	private RecipeDao recipeDao;
	@SpringBean
	private SportDao sportDao;
	@SpringBean
	private InOutHelperDao inOutHelperDao;
	@SpringBean
	private UserSportDao userSportDao;
	@SpringBean
	private UserRecipeDao userRecipeDao;
	@SpringBean
	private UserFoodDao userFoodDao;

	private final Map<String, List<String>> foodTypeMap = new HashMap<String, List<String>>(); // map:company->model

	private List<String> foodNames;
	private List<String> recipeNames;

	private long loggedUserId = ((MySession) getSession()).getUserID();

	private String selectedType;
	private int days = 7;

	private Model periodModel;
	private boolean isEmptyList = false;

	private TextField<Integer> mealWeight;

	private List<String> inOutDates = new ArrayList<String>();

	public MealActivityTrackerPanel(String id) {
		super(id);

		final FeedbackPanel feedback = new FeedbackPanel("feedback0");
		feedback.setOutputMarkupId(true);

		foodNames = foodDao.findAllNames();
		recipeNames = recipeDao.findAllNames();

		foodTypeMap.put("Potravina", foodNames);
		foodTypeMap.put("Recept", recipeNames);

		Calendar cal = Calendar.getInstance();
		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);

		final Date date = new Date(cal.getTimeInMillis());

		IModel<List<? extends String>> typeChoices = new AbstractReadOnlyModel<List<? extends String>>() {
			@Override
			public List<String> getObject() {
				return new ArrayList<String>(foodTypeMap.keySet());
			}

		};

		IModel<List<? extends String>> nameChoices = new AbstractReadOnlyModel<List<? extends String>>() {
			@Override
			public List<String> getObject() {
				List<String> names = foodTypeMap.get(selectedType);
				if (names == null) {
					names = Collections.emptyList();
				}
				return names;
			}

		};

		final DropDownChoice<String> types = new DropDownChoice<String>(
				"types", new PropertyModel<String>(this, "selectedType"),
				typeChoices);

		final DropDownChoice<String> names = new DropDownChoice<String>(
				"names", new Model<String>(""), nameChoices);
		names.setOutputMarkupId(true);

		mealWeight = new TextField<Integer>("mealWeight", new Model(""),
				Integer.class);
		mealWeight.add(new RangeValidator<Integer>(50, 1000));
		mealWeight.add(new CustomizedBeautyTip(
				"V pripade potraviny zadajte zjedene mnozstvo v g alebo ml")
				.getTip());

		Form<?> foodForm = new Form("food_form") {
			@Override
			protected void onSubmit() {
				if (!names.getInput().equals("")) {
					String pom = mealWeight.getInput().trim();
					boolean weightIsEmpty = (pom == "");
					boolean recipeIsSelected = getSelectedType().equals(
							"Recept");
					boolean foodIsSelected = getSelectedType().equals(
							"Potravina");

					if ((recipeIsSelected)
							|| ((foodIsSelected) && (!weightIsEmpty))) {
						if (recipeIsSelected) {
							String recipeName = names.getConvertedInput();
							Recipe recipe = recipeDao.findName(recipeName);
							UserRecipe userRecipe = new UserRecipe();

							userRecipe.setDate(date);
							userRecipe.setUser(userDao
									.load(loggedUserId));
							userRecipe.setUserId(loggedUserId);
							// TODO WTF
							userRecipe.setDayTime(0);
							userRecipe.setTotalCalories(recipe.getCalories());
							userRecipe.setRecipe(recipe);
							long idRecipe = recipe.getId();
							userRecipe.setRecipeId(idRecipe);
							userRecipeDao.save(userRecipe);

							userDao.load(loggedUserId).getRecipes().add(userRecipe);
							recipe.getUsers().add(userRecipe);
						} else {
							String foodName = names.getConvertedInput();
							Food food = foodDao.findName(foodName);
							UserFood userFood = new UserFood();

							userFood.setDate(date);
							userFood.setUser(userDao
									.load(loggedUserId));
							userFood.setUserId(loggedUserId);
							// TODO WTF
							userFood.setQuantity(Integer.parseInt(mealWeight
									.getInput()));
							int foodCalories = food.getCalories();
							userFood.setTotalCalories(foodCalories
									* (Integer.parseInt(mealWeight.getInput()) / 100));
							userFood.setFood(food);
							long idFood = food.getId();
							userFood.setFoodId(idFood);
							userFoodDao.save(userFood);
							
							userDao.load(loggedUserId).getFoods().add(userFood);
							food.getUsers().add(userFood);
						}
						int totalTodayRec = userRecipeDao
								.sumUserToday(loggedUserId);
						int totalTodayFood = userFoodDao
								.sumUserToday(loggedUserId);
						InOutHelper todayHelper = inOutHelperDao
								.findByUserToday(loggedUserId);
						if (todayHelper != null)
							todayHelper.setCaloriesIn(totalTodayRec
									+ totalTodayFood);
						else {
							InOutHelper newHelper = new InOutHelper();
							newHelper.setDate(date);
							newHelper.setCaloriesIn(totalTodayRec
									+ totalTodayFood);
							newHelper.setUsrid(((MySession) getSession())
									.getUserID());
							inOutHelperDao.save(newHelper);
						}

						feedback.info("Jedlo bolo pridane k zjedenym");
						
						drawChart(getDays());
						showTodayDatas();
					} else {
						feedback.error("Nezadal si davku jedla");
					}
				} else
					feedback.error("Vyber jedlo zo zoznamu");
			}
		};
		foodForm.add(mealWeight);
		foodForm.add(types);
		foodForm.add(names);
		add(foodForm);

		types.add(new AjaxFormComponentUpdatingBehavior("onchange") {
			@Override
			protected void onUpdate(AjaxRequestTarget target) {
				target.addComponent(names);
			}
		});

		final List<Sport> listOfSports = sportDao.findAll();

		List<String> sportsNames = new ArrayList<String>();
		for (Sport s : listOfSports) {
			sportsNames.add(s.getName());
		}

		Model model = new Model("");
		final DropDownChoice sportSelectChoice = new DropDownChoice(
				"sportSelectChoice", model, sportsNames);

		final TextField<Integer> durationField = new TextField<Integer>(
				"duration", new Model(""), Integer.class);
		durationField.add(new MinimumValidator<Integer>(0));

		Form sportsForm = new Form("sports_form") {
			@Override
			protected void onSubmit() {
				if (!sportSelectChoice.equals("")
						&& !durationField.getInput().equals("")) {
					Sport selectedSport = sportDao
							.findName((String) sportSelectChoice
									.getModelObject());
					if (selectedSport != null) {
						double pom = (Integer
								.parseInt(durationField.getInput())) / 60.0;
						int totalCalories = (int) Math.round(pom
								* selectedSport.getCalories());
						int inKJ = Math.round((float) (totalCalories * 4.19));
						UserSport userSport = new UserSport();
						userSport.setDate(date);
						userSport.setDuration(Integer.parseInt(durationField
								.getInput()));
						userSport.setUser(userDao.load(loggedUserId));
						userSport.setSport(selectedSport);
						userSport.setUser_id(loggedUserId);
						userSport.setSport_id(selectedSport.getId());
						userSport.setTotalCalories(inKJ);

						userSportDao.save(userSport);
						
						userDao.load(loggedUserId).getSports().add(userSport);
						selectedSport.getUsers().add(userSport);

						InOutHelper todayInOut = inOutHelperDao
								.findByUserToday(loggedUserId);
						int out = userSportDao.sumUserToday(loggedUserId);
						if (todayInOut != null) {
							todayInOut.setCaloriesOut(out);
						} else {
							InOutHelper newInOut = new InOutHelper();
							newInOut.setDate(date);
							newInOut.setUsrid(loggedUserId);
							newInOut.setCaloriesOut(out);
							inOutHelperDao.save(newInOut);
						}
						feedback.info("Vybrany sport bol pridany k aktivitam");
					}
					drawChart(getDays());
					showTodayDatas();
				} else {
					feedback.error("Zadaj vsetky hodnoty.");
				}
			}
		};

		sportsForm.add(durationField);
		sportsForm.add(sportSelectChoice);
		add(sportsForm);
		add(feedback);
		drawChart(getDays());

		periodModel = new Model("poslednych 7 dni");
		String[] selectOptions = new String[] { "poslednych 7 dni",
				"poslednych 30 dni", "od datumu registracie" };
		DropDownChoice ddc = new DropDownChoice("date1IntervalChoice",
				periodModel, Arrays.asList(selectOptions));
		Form<?> datePeriodForm = new Form<Void>("date1PeriodForm") {
			@Override
			protected void onSubmit() {
				String str = (String) periodModel.getObject();
				if (str.trim().equals("poslednych 7 dni")) {
					setDays(7);
				} else if (str.trim().equals("poslednych 30 dni")) {
					setDays(30);
				} else {
					setDays(100);
				}
				drawChart(getDays());
			}
		};
		datePeriodForm.add(ddc);
		add(datePeriodForm);

		showTodayDatas();

		final SimpleDateFormat sdf0 = new SimpleDateFormat("dd.MM.yyyy");

		List<Date> tmpDates = inOutHelperDao.findLast30Dates(loggedUserId);
		for (Date d : tmpDates) {
			inOutDates.add(sdf0.format(d));
		}
		Model dateModel = new Model(sdf0.format(date));
		final DropDownChoice dateChoice = new DropDownChoice("selectDay",
				dateModel, inOutDates);

		Form<?> showDatasForm = new Form<Void>("showDatasForm") {
			@Override
			protected void onSubmit() {
				String selected = dateChoice.getConvertedInput().toString();
				Date date1 = null;
				try {
					date1 = sdf0.parse(selected);
				} catch (ParseException e) {

				}
				showDatasByDate(date1);
			};
		};

		showDatasForm.add(dateChoice);
		add(showDatasForm);
		showDatasByDate(date);
	}

	private void showTodayDatas() {
		Calendar c = Calendar.getInstance();
		c.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
				c.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
		final Date date = new Date(c.getTimeInMillis());

		List<Object[]> list0 = userFoodDao.findAllByDate(loggedUserId, date);
		List<Object[]> list1 = userRecipeDao.findAllByDate(loggedUserId, date);
		List<Object[]> list2 = userSportDao.findAllByDate(loggedUserId, date);

		ListView<Object[]> lv0 = new ListView<Object[]>("userFoodRow0", list0) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("foodname0", ((Food) obj[0]).getName()));
				item.add(new Label("weight0", ((UserFood) obj[1]).getQuantity()+""));
				item.add(new Link("delete0") {
					@Override
					public void onClick() {
						Food food = (Food) obj[0];
						UserFood userFood = (UserFood) obj[1];
						long foodId = food.getId();
						
						food.getUsers().remove(userFood);
						userDao.load(loggedUserId).getRecipes().remove(userFood);
						userFoodDao.delete(userFood);
						
						InOutHelper todayInOut = inOutHelperDao
								.findByUserToday(loggedUserId);
						int inRecipe = userFoodDao.sumUserToday(loggedUserId);
						int inFood = userRecipeDao.sumUserToday(loggedUserId);
						
						todayInOut.setCaloriesIn(inRecipe+inFood);
						
						drawChart(getDays());
						showTodayDatas();
					}
				});
			}
		};

		ListView<Object[]> lv1 = new ListView<Object[]>("userRecipeRow0", list1) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("recipename0", ((Recipe) obj[0]).getName()));
				item.add(new Link("delete1") {
					@Override
					public void onClick() {
						Recipe recipe = (Recipe) obj[0];
						UserRecipe userRecipe = (UserRecipe) obj[1];
						long recipeId = recipe.getId();
						
						recipe.getUsers().remove(userRecipe);
						userDao.load(loggedUserId).getRecipes().remove(userRecipe);
						userRecipeDao.delete(userRecipe);
						
						InOutHelper todayInOut = inOutHelperDao
								.findByUserToday(loggedUserId);
						int inRecipe = userFoodDao.sumUserToday(loggedUserId);
						int inFood = userRecipeDao.sumUserToday(loggedUserId);
						
						todayInOut.setCaloriesIn(inRecipe+inFood);
						
						drawChart(getDays());
						showTodayDatas();
						
					}
				});
			}
		};

		ListView<Object[]> lv2 = new ListView<Object[]>("userSportRow0", list2) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("sportname0", ((Sport) obj[0]).getName()));
				item.add(new Label("duration0", ((UserSport) obj[1]).getDuration()+""));
				item.add(new Link("delete2") {
					@Override
					public void onClick() {
						UserSport userSport = (UserSport) obj[1];
						Sport selectedSport = (Sport) obj[0];
						long sportId = selectedSport.getId();
						
						selectedSport.getUsers().remove(userSport);
						userDao.load(loggedUserId).getSports().remove(userSport);
						userSportDao.delete(userSport);

						InOutHelper todayInOut = inOutHelperDao
								.findByUserToday(loggedUserId);
						int out = userSportDao.sumUserToday(loggedUserId);

						todayInOut.setCaloriesOut(out);

						drawChart(getDays());
						showTodayDatas();
					}
				});
			}
		};

		boolean bool = true;

		try {
			remove(lv0);
			remove(lv1);
			remove(lv2);
			bool = false;
		} catch (Exception e) {
			bool = false;
		}

		if (!bool) {
			add(lv0);
			add(lv1);
			add(lv2);
		}
	}

	private void showDatasByDate(final Date date) {
		List<Object[]> list3 = userFoodDao.findAllByDate(loggedUserId, date);
		List<Object[]> list4 = userRecipeDao.findAllByDate(loggedUserId, date);
		List<Object[]> list5 = userSportDao.findAllByDate(loggedUserId, date);

		ListView<Object[]> lv3 = new ListView<Object[]>("userFoodRow1", list3) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("foodname1", ((Food) obj[0]).getName()));
				item.add(new Label("weight1", ((UserFood) obj[1]).getQuantity()+""));
			}
		};

		ListView<Object[]> lv4 = new ListView<Object[]>("userRecipeRow1", list4) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("recipename1", ((Recipe) obj[0]).getName()));
			}
		};

		ListView<Object[]> lv5 = new ListView<Object[]>("userSportRow1", list5) {
			protected void populateItem(ListItem item) {
				final Object[] obj = (Object[]) item.getModelObject();
				item.add(new Label("sportname1", ((Sport) obj[0]).getName()));
				item.add(new Label("duration1", ((UserSport) obj[1]).getDuration()+""));
			}
		};

		boolean bool = true;

		try {
			remove(lv3);
			remove(lv4);
			remove(lv5);
			bool = false;
		} catch (Exception e) {
			bool = false;
		}

		if (!bool) {
			add(lv3);
			add(lv4);
			add(lv5);
		}

	}

	private void drawChart(int days) {
		isEmptyList = false;
		List<Number> ins = new ArrayList<Number>();
		List<Number> outs = new ArrayList<Number>();
		List<Number> diffs = new ArrayList<Number>();

		int maxIn = 0;
		int maxOut = 0;
		int maxDiff = 0;

		List<Object[]> lst = inOutHelperDao.findByUserLastXDays(loggedUserId,
				days - 1);

		String[] dates = null;
		int i = 0;
		int sumIn = 0;
		int sumOut = 0;
		int diff = 0;
		SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
		Date date0 = new Date();
		if (!lst.isEmpty()) {
			isEmptyList = false;
			dates = new String[lst.size()];
			for (Object[] obj : lst) {
				date0 = (Date) obj[0];

				dates[i] = sdf.format(date0);
				sumIn = Integer.parseInt(obj[1].toString());
				sumOut = Integer.parseInt(obj[2].toString());
				diff = sumIn - sumOut;

				ins.add(sumIn);
				if (days >= 8)
					outs.add(-sumOut);
				else
					outs.add(sumOut);
				diffs.add(diff);

				if (sumIn >= maxIn)
					maxIn = sumIn;
				if (sumOut >= maxOut)
					maxOut = sumOut;
				if ((sumOut - sumIn) >= maxDiff)
					maxDiff = sumOut - sumIn;
				i++;
			}
		} else {
			isEmptyList = true;
			dates = new String[1];
			dates[0] = sdf.format(date0);
			ins.add(0);
			outs.add(0);
			diffs.add(0);
			maxIn = maxOut = maxDiff = 0;
		}

		Chart chart;
		OpenFlashChart openFlsChart = null;
		YAxis yaxis = new YAxis();
		yaxis.setSteps(500);
		XAxis xaxis = new XAxis();
		XAxisLabels xaxisLab = new XAxisLabels();
		xaxisLab.setRotation(Rotation.VERTICAL);
		xaxisLab.addLabels(dates);
		xaxis.setXAxisLabels(xaxisLab);

		if (days >= 8) {
			AreaHollowChart hollow1 = new AreaHollowChart();
			hollow1.addValues(ins);
			hollow1.setColour("#FF0000");
			hollow1.setTooltip("Prijate kJ:#val#");
			hollow1.setText("Prijate kJ");
			hollow1.setFontSize(15);
			hollow1.setDotSize(2);
			hollow1.setHaloSize(1);

			AreaHollowChart hollow2 = new AreaHollowChart();
			hollow2.addValues(outs);
			hollow2.setColour("#008000");
			hollow2.setTooltip("Spalene kJ:#val#");
			hollow2.setText("Spalene kJ");
			hollow2.setFontSize(15);
			hollow2.setDotSize(2);
			hollow2.setHaloSize(1);

			LineChart lineChart = new LineChart(LineChart.Style.NORMAL);
			lineChart.addValues(diffs);
			lineChart.setColour("#000000");
			lineChart.setTooltip("Vysledne kJ:#val#");
			lineChart.setText("Vysledne kJ");
			lineChart.setAlpha(0.1f);
			lineChart.setFontSize(15);
			lineChart.setDotSize(2);
			lineChart.setHaloSize(1);

			chart = new Chart();
			chart.addElements(hollow1, hollow2, lineChart);
			chart.setBackgroundColour("#ffffff");
			yaxis.setMin(-maxOut - 50);
			yaxis.setMax(maxIn + 50);
			chart.setYAxis(yaxis);
			chart.setXAxis(xaxis);

			openFlsChart = new OpenFlashChart("chart1", 750, 450, chart);
		} else {
			BarChart bar1 = new BarChart(Style.GLASS);
			bar1.addValues(ins);
			bar1.setAlpha(0.1f);
			bar1.setColour("#FF0000");
			bar1.setFontSize(15);
			bar1.setText("Prijate kJ");
			bar1.setTooltip("Prijate kJ:#val#");

			BarChart bar2 = new BarChart(Style.GLASS);
			bar2.addValues(outs);
			bar2.setAlpha(0.1f);
			bar2.setColour("#008000");
			bar2.setFontSize(15);
			bar2.setText("Spalene kJ");
			bar2.setTooltip("Spalene kJ:#val#");

			BarChart bar3 = new BarChart(Style.GLASS);
			bar3.addValues(diffs);
			bar3.setAlpha(0.1f);
			bar3.setColour("#000000");
			bar3.setFontSize(15);
			bar3.setText("Vysledne kJ");
			bar3.setTooltip("Vysledne kJ:#val#");

			chart = new Chart();
			chart.addElements(bar1, bar2, bar3);
			chart.setBackgroundColour("#ffffff");
			yaxis.setMin(-maxDiff - 50);
			if (maxIn > maxOut)
				yaxis.setMax(maxIn + 50);
			else
				yaxis.setMax(maxOut + 50);
			chart.setYAxis(yaxis);
			chart.setXAxis(xaxis);

			openFlsChart = new OpenFlashChart("chart1", 750, 450, chart);
		}

		if (isEmptyList) {
			openFlsChart.setVisible(false);
		} else {
			openFlsChart.setVisible(true);
		}

		boolean bool = true;

		try {
			remove(openFlsChart);
			bool = false;
		} catch (Exception e) {
			bool = false;
		}

		if (!bool) {
			add(openFlsChart);
		}

	}

	/**
	 * @return Currently selected make
	 */
	public String getSelectedType() {
		return selectedType;
	}

	/**
	 * @param selectedMake
	 *            The make that is currently selected
	 */
	public void setSelectedType(String selectedType) {
		this.selectedType = selectedType;
	}

	public int getDays() {
		return days;
	}

	public void setDays(int days) {
		this.days = days;
	}

	public boolean isEmptyList() {
		return isEmptyList;
	}

	public void setEmptyList(boolean isEmptyList) {
		this.isEmptyList = isEmptyList;
	}

}
